package com.hb.B_面向对象.C_数据类

/**
 * 我们经常创建一些只保存数据的类。 在这些类中，一些标准函数往往是从数据机械推导而来的。
 * 在 Kotlin 中，这叫做 数据类 并标记为 data
 */
fun main() {
    val person1 = Person("John")
    val person2 = Person("John")
    person1.age = 10
    person2.age = 20
    //true
    println(person1==person2)

    //复制
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2)
    println(jack==olderJack)//false 如果复制时，没有修改年龄就是同一个对象

    //数据类与解构声明,为数据类生成的 Component 函数 使它们可在解构声明中使用：
    val jane = User("Jane", 35)
    val (name, age) = jane
    println("$name, $age years of age") // 输出 "Jane, 35 years of age"

    val person21 = Person2("java",1,18)
    println("person21.age:${person21.age} ,${person21.hashCode()},${person21.toString()}")

    //系统标准库中的标准数据类 标准库提供了 Pair 和 Triple
    val pair = Pair(1, 2)//实例
    val triple = Triple(1, 2, 3)//实例
    println("$pair \t $triple")//打印：即调用了各自的toString()方法
    println(pair.toList())//转换成List集合
    println(triple.toList())//转换成List集合
    println(pair.to(3))//Pair类特有: 其作用是把参数Pair类中的第二个参数替换  ((1, 2), 3)


}

/**
 * 编译器自动从主构造函数中声明的所有属性导出以下成员：
 * 1. equals()/hashCode() 对；
 * 2. toString() 格式是 "User(name=John, age=42)"；
 * 3. componentN() 函数 按声明顺序对应于所有属性；
 * 4. copy() 函数
 *
 * 为了确保生成的代码的一致性以及有意义的行为，数据类必须满足以下要求：
 * 1. 主构造函数需要至少有一个参数；
 * 2. 主构造函数的所有参数需要标记为 val 或 var；
 * 3. 数据类不能是抽象、开放、密封或者内部的；（在1.1之前）数据类只能实现接口。
 *
 * 此外，成员生成遵循关于成员继承的这些规则：
 * 1. 如果在数据类体中有显式实现 equals()、 hashCode() 或者 toString()，或者这些函数在父类中有 final 实现，那么不会生成这些函数，而会使用现有函数
 * 2. 如果超类型具有 open 的 componentN() 函数并且返回兼容的类型， 那么会为数据类生成相应的函数，并覆盖超类的实现。
 *    如果超类型的这些函数由于签名不兼容或者是 final 而导致无法覆盖，那么会报错；
 * 3. 从一个已具 copy(……) 函数且签名匹配的类型派生一个数据类在 Kotlin 1.2 中已弃用，并且在 Kotlin 1.3 中已禁用。
 *
 * 4. 不允许为 componentN() 以及 copy() 函数提供显式实现。自 1.1 起，数据类可以扩展其他类
 *
 * 在 JVM 中，如果生成的类需要含有一个无参的构造函数，则所有的属性必须指定默认值。
 * data class User(val name: String = "", val age: Int = 0)
 */
data class User(val name: String, val age: Int){

}


/**
 * 在类体中声明的属性
 * 请注意，对于那些自动生成的函数，编译器只使用在主构造函数内部定义的属性。如需在生成的实现中排除一个属性，请将其声明在类体中
 * 在 toString()、 equals()、 hashCode() 以及 copy() 的实现中只会用到 name 属性，并且只有一个 component 函数 component1()。
 * 虽然两个 Person 对象可以有不同的年龄，但它们会视为相等。
 */
data class Person(val name: String) {
    var age: Int = 0
}

/**
 * 在很多情况下，我们需要复制一个对象改变它的一些属性，但其余部分保持不变。 copy() 函数就是为此而生成。对于上文的 User 类，其实现会类似下面这样：
 * fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
 */

/**
 * 在Java中，或者在我们平时的Android开发中，为了解析后台人员给我们提供的接口返回的Json字符串，
 * 我们会根据这个字符串去创建一个类或者实例对象，在这个类中，只包含了一些我们需要的数据，以及为了处理这些数据而所编写的方法。
 * 这样的类，在Kotlin中就被称为数据类。
 *
 * 声明数据类的关键字为：data
 *
 * 声明格式
 * data class 类名(var param1 ：数据类型,...){}
 * 或者
 * data class 类名 可见性修饰符 constructor(var param1 : 数据类型 = 默认值,...)
 *
 * data为声明数据类的关键字，必须书写在class关键字之前。
 * 在没有结构体的时候，大括号{}可省略。
 * 构造函数中必须存在至少一个参数，并且必须使用val或var修饰。这一点在下面数据类特性中会详细讲解。
 * 参数的默认值可有可无。（若要实例一个无参数的数据类，则就要用到默认值）
 */


/**
 * 定义一个名为Person的数据类
 * 数据类也有其约定俗成的一些规定，这只是为增加代码的阅读性。即，当构造函数中的参过多时，为了代码的阅读性，一个参数的定义占据一行。
 *
 * 编辑器为我们做的事情
 *
 * 当我们声明一个数据类时，编辑器自动为这个类做了一些事情，不然它怎么又比Java简洁呢。
 * 它会根据主构造函数中所定义的所有属性自动生成下列方法：
 * 生成equals()函数与hasCode()函数
 * 生成toString()函数，由类名（参数1 = 值1，参数2 = 值2，….）构成
 * 由所定义的属性自动生成component1()、component2()、…、componentN()函数，其对应于属性的声明顺序。
 * copy()函数。在下面会实例讲解它的作用。其中，当这些函数中的任何一个在类体中显式定义或继承自它的基类型，则不会生成该函数！！！
 *
 * 数据类有着和Kotlin其他类不一样的特性。除了含有其他类的一些特性外，还有着其独特的特点。并且也是数据类必须满足的条件：
 * 1. 主构造函数需要至少有一个参数
 * 2. 主构造函数的所有参数需要标记为 val 或 var；
 * 3. 数据类不能是抽象、开放、密封或者内部的；
 * 4. 数据类是可以实现接口的，如(序列化接口)，同时也是可以继承其他类的，如继承自一个密封类。
 *
 */
data class Person2(
    var name : String,
    val sex : Int,
    var age : Int
    )
